import requests as re  # 导入requests库用于HTTP请求
import time as t  # 导入time库用于等待时间

# 快速模幂运算函数
def fastModular(x):
    result = 1
    while (x[1] > 0):
        if (x[1] & 1):  # 如果指数x[1]是奇数
            result = result * x[0] % x[2]  # 计算结果的模x[2]
        x[1] = int(x[1] / 2)  # 指数减半
        x[0] = x[0] * x[0] % x[2]  # 底数平方并模x[2]
    return result  # 返回计算结果

# 字符串转换为数值函数
def str_to_num(strings):
    sum = 0
    lens = len(strings)
    for i in range(0, lens):
        sum += ord(strings[i]) * 256 ** (lens - i - 1)  # 将字符串转换为数值
    return sum  # 返回数值结果

# 登录密码编码函数
def encodeLogin(password):
    power = 65537  # 指数
    modulus = 135261828916791946705313569652794581721330948863485438876915508683244111694485850733278569559191167660149469895899348939039437830613284874764820878002628686548956779897196112828969255650312573935871059275664474562666268163936821302832645284397530568872432109324825205567091066297960733513602409443790146687029  # 模数
    return hex(fastModular([str_to_num(password), power, modulus]))  # 返回十六进制编码后的密码
# 加入游戏函数
def join_game(user, myHexPass):
    url = 'http://183.175.12.27:8002/join_game'  # 游戏加入的URL
    param = {
        'user': user,
        'password': myHexPass,
        'data_type': 'json'
    }
    getHtml = re.get(url, params=param)  # 发送HTTP GET请求
    print(f"打开新游戏 {getHtml.text}")  # 打印服务器返回信息
    return getHtml  # 返回HTTP响应对象
# 检查游戏状态函数
def check_game(game_id):
    url = 'http://183.175.12.27:8002/check_game/' + str(game_id)  # 检查游戏状态的URL
    getState = re.get(url)  # 发送HTTP GET请求
    return getState  # 返回HTTP响应对象
# 下棋函数
def play_game(user, myHexPass, game_id, coord):
    url = 'http://183.175.12.27:8002/play_game/' + str(game_id)  # 下棋的URL
    param = {
        'user': user,
        'password': myHexPass,
        'data_type': 'json',
        'coord': coord
    }
    re.get(url, params=param)  # 发送HTTP GET请求

# 根据坐标获取索引号函数
def getIndexNum(coords):
    return (ord(coords[0]) - ord('a')) * 16 + ord(coords[1]) - ord('a')

# 获取所有可能的坐标字符串函数
def allIndexStr():
    spot = []
    for i in range(0, 15):
        for j in range(0, 16):
            spot.append(chr(i + 97) + chr(j + 97))  # 生成所有可能的坐标字符串
    return spot  # 返回所有坐标字符串列表

# 根据给定坐标和棋盘获取四条线函数
def getLine(coord, board):
    line = ['', '', '', '']
    i = 0
    while (i != 15):
        if ord(coord[1]) - ord('a') - 7 + i in range(0, 15):
            line[0] += board[(ord(coord[0]) - ord('a')) * 16 + ord(coord[1]) - ord('a') - 7 + i]
        else:
            line[0] += ' '
        if ord(coord[0]) - ord('a') - 7 + i in range(0, 15):
            line[2] += board[(ord(coord[0]) - ord('a') - 7 + i) * 16 + ord(coord[1]) - ord('a')]
        else:
            line[2] += ' '
        if ord(coord[1]) - ord('a') - 7 + i in range(0, 15) and ord(coord[0]) - ord('a') - 7 + i in range(0, 15):
            line[1] += board[(ord(coord[0]) - ord('a') - 7 + i) * 16 + ord(coord[1]) - ord('a') - 7 + i]
        else:
            line[1] += ' '
        if ord(coord[1]) - ord('a') + 7 - i in range(0, 15) and ord(coord[0]) - ord('a') - 7 + i in range(0, 15):
            line[3] += board[(ord(coord[0]) - ord('a') - 7 + i) * 16 + ord(coord[1]) - ord('a') + 7 - i]
        else:
            line[3] += ' '
        i += 1
    return line  # 返回四条线字符串列表

# 判断下一步是'MO'还是'OM'函数
def judge(testOrder):
    if (len(testOrder) // 2) % 2 == 0:
        return 'MO'
    else:
        return 'OM'

# 规则及其对应分数函数
def RuleWithPoints():
    RWP = {
        ("CMMMM", "MCMMM", "MMCMM", "MMMCM", "MMMMC"): 10000,
        ("COOOO", "OCOOO", "OOCOO", "OOOCO", "OOOOC"): 6000,
        (".CMMM.", ".MCMM.", ".MMCM.", ".MMMC."): 5000,
        ("COOO.", ".OOOC", ".OOCO.", ".OCOO."): 2500,
        ("OCMMM.", "OMCMM.", "OMMCM.", "OMMMC.", ".CMMMO", ".MCMMO", ".MMCMO", ".MMMCO"): 2000,
        (".MMC.", ".MCM.", ".CMM."): 400,
        (".OOC", "COO.", "MOOOC", "COOOM"): 400,
        (".MMCO", ".MCMO", ".CMMO", "OMMC.", "OMCM.", "OCMM.", "MOOC", "COOM"): 200,
        (".MC.", ".CM."): 50, ("."): 1,
    }
    return RWP  # 返回规则及其对应分数的字典

# 评估棋盘得分函数
def evaluate_board(board, RWP):
    points = 0
    board_str = ''.join(board)
    for rules, value in RWP.items():
        for rule in rules:
            points += board_str.count(rule) * value  # 计算每条规则在棋盘上出现的次数乘以对应分数，并累加得分
    return points  # 返回总得分

# 获取可用移动列表函数
def get_valid_moves(board):
    return [i for i, cell in enumerate(board) if cell == '.']  # 返回所有空格的索引列表

# 执行移动函数
def make_move(board, move, player):
    return board[:move] + player + board[move + 1:]  # 在指定位置执行移动，player为'M'或'O'

# 判断是否为终止节点函数
def is_terminal_node(board):
    return 'CMMMM' in board or 'COOOO' in board or '.' not in board  # 判断是否有五子连珠或棋盘已满

# 极小极大算法函数
def minimax(board, depth, alpha, beta, maximizingPlayer, RWP, memo):
    board_tuple = tuple(board)  # 将棋盘状态转换为元组作为缓存的键
    if board_tuple in memo:  # 如果已经计算过该状态，直接返回缓存结果
        return memo[board_tuple]
    if depth == 0 or is_terminal_node(board):  # 如果达到最大深度或是终止节点
        eval = evaluate_board(board, RWP)  # 计算当前棋盘得分
        memo[board_tuple] = eval  # 缓存当前状态的评估结果
        return eval  # 返回评估结果
    if maximizingPlayer:  # 如果是最大化玩家的回合
        maxEval = float('-inf')
        for move in get_valid_moves(board):  # 遍历所有可能的移动
            new_board = make_move(board, move, 'M')  # 假设'M'下棋
            eval = minimax(new_board, depth - 1, alpha, beta, False, RWP, memo)  # 递归调用极小化玩家
            maxEval = max(maxEval, eval)  # 更新最大值
            alpha = max(alpha, eval)  # 更新alpha值
            if beta <= alpha:  # 剪枝条件
                break
        memo[board_tuple] = maxEval  # 缓存当前状态的评估结果
        return maxEval  # 返回最大值
    else:  # 如果是最小化玩家的回合
        minEval = float('inf')
        for move in get_valid_moves(board):  # 遍历所有可能的移动
            new_board = make_move(board, move, 'O')  # 假设'O'下棋
            eval = minimax(new_board, depth - 1, alpha, beta, True, RWP, memo)  # 递归调用极大化玩家
            minEval = min(minEval, eval)  # 更新最小值
            beta = min(beta, eval)  # 更新beta值
            if beta <= alpha:  # 剪枝条件
                break
        memo[board_tuple] = minEval  # 缓存当前状态的评估结果
        return minEval  # 返回最小值

# 获取最佳移动函数
def get_best_move(board, RWP, depth):
    best_move = None
    maxEval = float('-inf')
    memo = {}
    for move in get_valid_moves(board):  # 遍历所有可能的移动
        new_board = make_move(board, move, 'M')  # 假设'M'下棋
        eval = minimax(new_board, depth - 1, float('-inf'), float('inf'), False, RWP, memo)  # 使用极小极大算法计算评估值
        if eval > maxEval:  # 更新最佳移动和最大评估值
            maxEval = eval
            best_move = move
    return best_move  # 返回最佳移动的索引

# 获取最大坐标及其评估分数函数
def getMaxCoords(Order, RWP, indexSrc):
    board = '...............\n' * 15  # 初始棋盘状态
    step = 0
    BW = judge(Order)  # 判断玩家顺序

    for i in range(0, len(Order), 2):
        index = getIndexNum(Order[i:i + 2])  # 获取当前下棋位置的索引
        if (step % 2) == 0:
            board = board[0: index] + BW[0] + board[index + 1:]  # 在棋盘上放置'M'
        else:
            board = board[0: index] + BW[1] + board[index + 1:]  # 在棋盘上放置'O'
        step += 1
    print(board)  # 打印棋盘用于测试

    maxCoord = ''
    maxPoints = 0
    for i in range(0, len(board)):
        if board[i] == '.':  # 如果是空白位置
            tempBoard = board[:i] + 'C' + board[i + 1:]  # 假设'M'在此位置下棋
            coord = indexSrc[i]  # 获取当前位置的坐标字符串
            lines4 = ','.join(getLine(coord, tempBoard))  # 获取四条线的字符串
            points = 0
            for rules, value in RWP.items():
                for rul in range(0, len(rules)):
                    if rules[rul] in lines4:  # 如果规则在四条线中出现
                        points += value * lines4.count(rules[rul])  # 增加相应的分数

            if points > maxPoints:  # 如果当前位置的分数最高
                maxPoints = points
                maxCoord = coord  # 更新最大分数及其坐标

    print(f"{maxCoord} {maxPoints}", end=' ')  # 打印最佳坐标及其分数
    return maxCoord  # 返回最佳坐标字符串

# 用户信息和密码
user = '0211423595'
password = 'ys100820'
myHexPass = encodeLogin(password)  # 加密后的密码
RWP = RuleWithPoints()  # 规则及其对应分数字典
indexSrc = allIndexStr()  # 所有可能的坐标字符串列表

game_id = join_game(user, myHexPass).json()["game_id"]  # 加入游戏并获取游戏ID
state = check_game(game_id).json()  # 获取当前游戏状态

print("正在寻找游戏对手...")
while state['ready'] == "False":  # 等待游戏准备就绪
    state = check_game(game_id).json()  # 获取当前游戏状态
    print(state['ready'], end=" ")  # 打印游戏状态信息
    t.sleep(5)  # 等待5秒

if state['creator'] != user:
    opponent = state['creator']  # 如果当前用户不是创建者，对手是创建者
else:
    opponent = state['opponent_name']  # 否则对手是加入者

while state['ready'] == "True":  # 如果游戏准备就绪
    if state['current_turn'] == user:  # 如果轮到当前用户下棋
        order = state['board']  # 获取当前棋盘状态
        coord = getMaxCoords(order, RWP, indexSrc)  # 获取最佳下棋坐标
        play = play_game(user, myHexPass, game_id, coord)  # 执行下棋操作
        print(f"正在下棋 {coord}")  # 打印下棋信息
    else:
        print(f"等待 {opponent} 下棋")  # 打印等待对手下棋信息

    t.sleep(5)  # 等待5秒
    state = check_game(game_id).json()  # 获取当前游戏状态

    if state['winner'] != "None":  # 如果有胜者产生
        print(f"获胜者是 {state['winner']}")  # 打印胜者信息
        break  # 结束游戏循环
